package com.shadow.demo.component.variable;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.shadow.demo.common.enums.CacheEnum;
import com.shadow.demo.common.enums.DictParentEnums;
import com.shadow.demo.common.util.JacksonUtils;
import com.shadow.demo.data.bo.Query;
import com.shadow.demo.data.redis.RedisService;
import com.shadow.demo.data.variable.VariableEntity;
import com.shadow.demo.data.variable.VariableMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Service
public class VariableService extends ServiceImpl<VariableMapper, VariableEntity> implements IService<VariableEntity> {

    /** 上级字典编码属性. **/
    private static final String PARENT_ID = "parent_id";
    /** 缓存超时时间. **/
    private static final Long NEVER_TIME_OUT = 0L;

    private final RedisService redisService;
    private final VariableMapper variableMapper;

    VariableService(RedisService redisService, VariableMapper variableMapper) {
        this.redisService = redisService;
        this.variableMapper = variableMapper;
    }

    /**
     * 判断字典值是否存在.
     * @author pbody
     * @date 15:54 2020/9/8
     * @param name 字典名称
     * @param value 字典值
     * @param parentId 上级字典ID
     * @param id 字典ID
     * @return boolean
     **/
    public boolean isExists(final String name, final String value, final String parentId, final String id) {
        boolean existsFlag = false;
        QueryWrapper<VariableEntity> dictionaryWrapper = new QueryWrapper<>();
        dictionaryWrapper.eq(name, value);
        dictionaryWrapper.eq(PARENT_ID, parentId);
        List<VariableEntity> dictList = list(dictionaryWrapper);
        if (CollectionUtils.isNotEmpty(dictList)) {
            existsFlag = true;
            if (StringUtils.isNotBlank(id) && id.equals(dictList.get(0).getId())) {
                existsFlag = false;
            }
        }
        return existsFlag;
    }

    /**
     * 获取字典项根据上级ID和字典值.
     * @author pbody
     * @date 15:55 2020/9/8
     * @param value 字典值
     * @param parentId 上级字典ID
     * @return net.zoneland.demo.dal.entity.sys.Dictionary
     **/
    public VariableEntity getByValue(final String parentId, final String value) {
        QueryWrapper<VariableEntity> dictionaryWrapper = new QueryWrapper<>();
        dictionaryWrapper.eq(PARENT_ID, parentId);
        dictionaryWrapper.eq("dict_value", value);
        List<VariableEntity> list = list(dictionaryWrapper);
        if (CollectionUtils.isNotEmpty(list)) {
            return list.get(0);
        }
        return null;
    }

    /**
     * 获取字典项根据上级ID和名称.
     * @author pbody
     * @date 15:55 2020/9/8
     * @param name 字典名称
     * @param parentId 上级字典ID
     * @return net.zoneland.demo.dal.entity.sys.Dictionary
     **/
    public VariableEntity getByName(final String parentId, final String name) {
        QueryWrapper<VariableEntity> dictionaryWrapper = new QueryWrapper<>();
        dictionaryWrapper.eq(PARENT_ID, parentId);
        dictionaryWrapper.eq("dict_name", name);
        return Optional.ofNullable(list(dictionaryWrapper)).filter(CollectionUtils::isNotEmpty)
                .map(list -> list.stream().findFirst().get()).orElse(null);
    }

    /**
     * 获取缓存的字典列表根据上级ID.
     * @author pbody
     * @date 15:55 2020/9/8
     * @param parentId 上级字典ID
     * @return java.util.List
     **/
    public List<VariableEntity> getByParentId(final String parentId) {
        return Collections.unmodifiableList(getCacheItems(parentId));
    }

    /**
     * 获取最新的字典列表（不走缓存）根据上级ID.
     * @author pbody
     * @date 15:55 2020/9/8
     * @param parentId 上级字典ID
     * @return java.util.List
     **/
    public List<VariableEntity> getByParentIdNow(final String parentId) {
        final QueryWrapper<VariableEntity> dictionaryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(parentId)) {
            dictionaryWrapper.eq(PARENT_ID, parentId);
        }
        dictionaryWrapper.orderByAsc("parent_id,orders");
        return Collections.unmodifiableList(list(dictionaryWrapper));
    }

    /**
     * 从缓存中获取字典数据：若从redis读取失败则从数据库中获取.
     * @date 15:57 2020/9/8
     * @param parentId 上级编码
     * @return java.util.List
     **/
    public List<VariableEntity> getCacheItems(final String parentId) {
        final String dicJson =  redisService.hget(CacheEnum.DICT_CACHE_PREFIX.getValue(), parentId);
        //先从缓存中胡获取数据字典
        if (StringUtils.isNotBlank(dicJson)) {
            return JacksonUtils.toTypeObject(dicJson, new TypeReference<List<VariableEntity>>() { });
        }
        log.info("{}的缓存读取为空：{}", CacheEnum.DICT_CACHE_PREFIX.getValue(), parentId);
        return getByParentIdNow(parentId);
    }

    /**
     * 保存数据字典.
     * @date 16:07 2020/9/8
     * @param dic 字典对象
     **/
    @Transactional(rollbackFor = Exception.class)
    public void saveDic(final VariableEntity dic) {
        //新增并更新缓存
        save(dic);
        updateCache(dic.getParentId());
    }

    /**
     * 修改数据字典.
     * @date 16:07 2020/9/8
     * @param dic 字典对象
     **/
    @Transactional(rollbackFor = Exception.class)
    public void updateDic(final VariableEntity dic) {
        //修改并更新缓存
        updateById(dic);
        updateCache(dic.getParentId());
    }

    /**
     * 删除数据字典.
     * @date 16:08 2020/9/8
     * @param id 字典ID
     **/
    public void deleteDic(final String id) {
        final VariableEntity item = getById(id);
        if (item != null) {
            //删除并更新缓存
            removeById(id);
            updateCache(item.getParentId());
        }
    }

    /**
     * 更新数据字典缓存.
     * @date 16:09 2020/9/8
     * @param parentId 上级字典编码ID
     **/
    private void updateCache(final String parentId) {
        redisService.hset(CacheEnum.DICT_CACHE_PREFIX.getValue(), parentId,
                JacksonUtils.toJsonString(getByParentIdNow(parentId)), NEVER_TIME_OUT);
    }

    /**
     * 第一次启动加载数据字典缓存，以parentId进行分组存储.
     * @Date 16:09 2020/9/8
     **/
    public void initAllCache() {
        final QueryWrapper<VariableEntity> dictionaryWrapper = new QueryWrapper<>();
        dictionaryWrapper.orderByAsc("parent_id,orders");
        Optional.ofNullable(list(dictionaryWrapper)).filter(CollectionUtils::isNotEmpty)
                .ifPresent(items -> saveCacheByParentId(items));
    }

    /**
     * 初始保存所有字典缓存.
     * @author pbody
     * @date 2:21 PM 2022/9/7
     * @param itemList 数据列表
     **/
    private void saveCacheByParentId(final List<VariableEntity> itemList) {
        final Map<String, List<VariableEntity>> dictDataMap = new HashMap<>();
        itemList.stream().forEach(dictItem -> {
            final List<VariableEntity> typeList = dictDataMap.get(dictItem.getParentId());
            if (CollectionUtils.isNotEmpty(typeList)) {
                typeList.add(dictItem);
            } else {
                //parentId第一个put进行
                final List<VariableEntity> newDicList = new ArrayList<>();
                newDicList.add(dictItem);
                dictDataMap.put(dictItem.getParentId(), newDicList);
            }
        });
        //根据上级ID分类存储
        final Iterator<?> iterator = dictDataMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, List<VariableEntity>> entry = (Map.Entry<String, List<VariableEntity>>) iterator.next();
            // 放入cache缓存
            redisService.hset(CacheEnum.DICT_CACHE_PREFIX.getValue(), entry.getKey(),
                    JacksonUtils.toJsonString(entry.getValue()), NEVER_TIME_OUT);
        }
    }

    /**
     * 获取当前权限下的可管理字典项.
     * @author pbody
     * @date 1:32 PM 2022/9/7
     * @param isAdmin 是否系统管理员
     * @return java.util.List
     **/
    public List<DictParentEnums> getParentAuthList(final Boolean isAdmin) {
        final List<DictParentEnums> allList = Arrays.asList(DictParentEnums.values());
        return Boolean.TRUE.equals(isAdmin) ? allList : allList.stream()
                .filter(dic -> Boolean.FALSE.equals(dic.getAdminAuth())).collect(Collectors.toList());
    }

    /**
     * 数据列表.
     **/
    public Page<VariableEntity> getList(final Map<String, Object> params, final boolean isAdmin) {
        final String value = (String) params.get("value");
        final String name = (String) params.get("name");
        final String parentId = (String) params.get("parentId");
        final LambdaQueryWrapper<VariableEntity> wrapper = new LambdaQueryWrapper<VariableEntity>()
                .eq(StringUtils.isNotBlank(value), VariableEntity::getValue, value)
                .eq(StringUtils.isNotBlank(name), VariableEntity::getName, name)
                .eq(StringUtils.isNotBlank(parentId), VariableEntity::getParentId, parentId);
        // 非系统管理员根据配置的权限来决定展现的字典项
        if (isAdmin) {
            final List<String> parentList = Stream.of(DictParentEnums.values())
                    .filter(enums -> BooleanUtils.isFalse(enums.getAdminAuth()))
                    .map(DictParentEnums::getValue).collect(Collectors.toList());
            wrapper.in(CollectionUtils.isNotEmpty(parentList), VariableEntity::getParentId, parentList);
        }
        wrapper.orderByAsc(VariableEntity::getParentId, VariableEntity::getOrders);
        // 转换部分值显示到前台
        Page<VariableEntity> page = variableMapper.selectPage(new Query<VariableEntity>(params).getPage(), wrapper);
        page.getRecords().forEach(dict -> {
            final String dicPid = dict.getParentId();
//            dict.setParentName(Optional.ofNullable(EnumUtils.getByValue(DictParentEnums.class, dicPid))
//                .map(DictParentEnums::getName).orElse(dicPid)
//            );
        });
        return page;
    }
}
